;注意:本程序中的延时部分 请使用者根据控制效果灵活修改BX的值(推荐值100H)
;对书本上的代码做了一些调整 使得用户交互更加友好
;三段分支可以考虑用循环合并(在改进代码中实现)
DATA SEGMENT
;-接口区-
IO8253A EQU 280H              ;计数器0地址
IO8253B EQU 281H              ;计数器1地址
IO8253C EQU 282H              ;计数器2地址
IO8253D EQU 283H              ;8253控制端口
;-提示信息区-
HC  DB 0AH,0DH,'$'            ;回车换行 用于接收用户输入后进行换行输出
TS  DB 0AH,0DH                ;菜单
    DB 'Press 1, lamb on once',0AH,0DH   
    DB 'Press 2, lamb on twice',0AH,0DH
    DB 'Press 3, lamb on third',0AH,0DH
    DB 'Press q, quit',0AH,0DH,'$'
ER  DB 'wrong key!',0AH,0DH,'$' ;提示用户按键错误 
S1  DB 'L ','$'   ;提示用户 当前进行的是哪一组闪烁任务
S2  DB 'LEC ','$'
S3  DB 'LS ','$' 
WAT DB 'is processing, please wait for a short time.',0AH,0DH,'$';提示用户等待
FNS DB 'has finished.',0AH,0DH,'$'   ;提示用户某任务结束了
TC DB 'Over',0AH,0DH,'$'           ;结束提示

DATA ENDS

CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
      ;数据段初始化
      MOV AX,DATA 
      MOV DS,AX
      
;ZL 择路 接收按键跳转指定模块
      ;输出菜单
ZL:   LEA DX,TS
      MOV AH,9
      INT 21H 
      ;键盘输入一个字符 存ASCII到AL
      MOV AH,1 
      INT 21H 
      ;输出换行
      LEA DX,HC
      MOV AH,9
      INT 21H
      ;依次比较 1 2 3 q
      CMP AL,31H
      JZ L          ;1 跳L 亮一下 
      CMP AL,32H
      JZ LEC        ;2 跳LEC 亮两下
      CMP AL,33H
      JZ LSNEXT     ;3 跳LS 亮三下
      CMP AL,71H
      JZ JSNEXT     ;q 跳JIESHU 结束
      ;提示 错误输入
      LEA DX,ER
      MOV AH,9
      INT 21H
      ;其他按键 跳ZL 用户重新输入
      JMP ZL        
LSNEXT: JMP LS       ;LS距离JZ语句超出跳转范围 此处用JMP中继
JSNEXT: JMP JIESHU   ;同JMP LS   

; L 亮一下
L:    ;提示用户等待
      LEA DX,S1 
      MOV AH,9
      INT 21H 
      LEA DX,WAT 
      MOV AH,9
      INT 21H
      ;向8253控制端口写30H(0011 0000) 设置(00)计数器0 工作方式 
      ;(11)先读写低字节 (000)方式零 (0)二进制数制
      MOV DX,IO8253D
      MOV AL,30H
      OUT DX,AL
      ;向计数器0写计数器初值0AAAAH
      MOV DX,IO8253A
      MOV AL,0AAH
      OUT DX,AL
      OUT DX,AL
      ;提示用户已经完成闪烁任务
      LEA DX,S1 
      MOV AH,9
      INT 21H 
      LEA DX,FNS 
      MOV AH,9
      INT 21H
      ;跳回ZL
      JMP ZL

; LEC 亮两下
LEC:  ;提示用户等待
      LEA DX,S2 
      MOV AH,9
      INT 21H 
      LEA DX,WAT 
      MOV AH,9
      INT 21H
      ;向8253控制端口写70H(0111 0000) 设置(01)计数器1 工作方式 
      ;(11)先读写低字节 (000)方式零 (0)二进制数制      
      MOV DX,IO8253D
      MOV AL,70H
      OUT DX,AL
      ;向计数器1写计数器初值0AAAAH
      MOV DX,IO8253B
      MOV AL,0AAH
      OUT DX,AL
      OUT DX,AL  
      ;延时 (CX)*(BX)个DEC指令时间
      MOV CX,0FFFFH
WAIT3: MOV BX,100H
WAIT4: DEC BX
       JNZ WAIT4
       LOOP WAIT3
      ;向计数器1再写一次计数器初值0AAAAH
       MOV DX,IO8253B 
       MOV AL,0AAH
       OUT DX,AL
       OUT DX,AL
      ;提示用户已经完成闪烁任务
      LEA DX,S2
      MOV AH,9
      INT 21H 
      LEA DX,FNS 
      MOV AH,9
      INT 21H
      ;跳回ZL
       JMP ZL


; LS 亮三下
LS:   ;提示用户等待
      LEA DX,S3 
      MOV AH,9
      INT 21H 
      LEA DX,WAT 
      MOV AH,9
      INT 21H
      ;向8253控制端口写0B0H(1011 0000) 设置(10)计数器2 工作方式 
      ;(11)先读写低字节 (000)方式零 (0)二进制数制      
       MOV DX,IO8253D
       MOV AL,0B0H
       OUT DX,AL
      ;向计数器2再写一次计数器初值0AAAAH
       MOV DX,IO8253C
       MOV AL,0AAH
       OUT DX,AL
       OUT DX,AL
      ;延时 (CX)*(BX)个DEC指令时间
       MOV CX,0FFFFH
WAIT1: MOV BX,100H
WAIT2: DEC BX
       JNZ WAIT2
       LOOP WAIT1
       ;向计数器2再写一次计数器初值0AAAAH
       MOV DX,IO8253C
       MOV AL,0AAH
       OUT DX,AL
       OUT DX,AL
      ;延时 (CX)*(BX)个DEC指令时间
       MOV CX,0FFFFH
WAIT11:MOV BX,100H
WAIT22:DEC BX
       JNZ WAIT22
       LOOP WAIT11
       ;向计数器2再写一次计数器初值0AAAAH
       MOV DX,IO8253C
       MOV AL,0AAH
       OUT DX,AL
       OUT DX,AL
      ;提示用户已经完成闪烁任务
      LEA DX,S3
      MOV AH,9
      INT 21H 
      LEA DX,FNS 
      MOV AH,9
      INT 21H
       ;跳回ZL
       JMP ZL

;JIESHU 结束
        ;输出结束信息 over
JIESHU: LEA DX,TC
        MOV AH,9
        INT 21H 
        ;退出程序
        MOV AH,4CH
        INT 21H
CODE ENDS
END START